package drds.plus.api;

import drds.plus.common.jdbc.Parameters;
import drds.plus.common.properties.ConnectionProperties;
import drds.plus.executor.ExecuteContext;
import drds.plus.executor.cursor.cursor.impl.result_cursor.ResultCursor;
import drds.plus.executor.cursor.cursor.impl.result_cursor.RowDataListResultCursor;
import drds.plus.sql_process.optimizer.OptimizerContext;
import drds.plus.sql_process.type.Type;
import drds.plus.util.GeneralUtil;
import lombok.extern.slf4j.Slf4j;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class Execute {

    private final String applicationId;
    protected String sql;
    protected SessionManager sessionManager;
    protected SessionImpl session;
    protected ExecuteContext executeContext = null;
    /**
     * 更新计数，如果执行了多次，那么这个值只会返回最后一次执行的结果。 如果是一个query，那么返回的数据应该是-1
     */
    protected int updateCount;
    /**
     * 经过计算后的结果集，允许使用 getResult函数调用. 一个statement只允许有一个结果集
     */
    protected ResultCursor currentResultSet;
    /**
     * 返回generated slotKeys
     */
    protected RowDataSet generatedKeysResults;
    protected Map<String, Object> extraCmd = new HashMap<String, Object>(4);
    /**
     * 当前statment 是否是关闭的
     */
    protected boolean closed;
    protected int maxFieldSize;
    protected int maxRows;
    protected int queryTimeOut;

    /**
     * 貌似是只有存储过程中会出现多结果集 因此不支持
     */
    protected boolean moreResults;

    public Execute(SessionManager sessionManager, SessionImpl sessionImpl, ExecuteContext executeContext) {
        this.sessionManager = sessionManager;
        this.session = sessionImpl;
        this.executeContext = executeContext;
        this.applicationId = executeContext.getApplicationId();
    }

    public Execute(SessionManager sessionManager, SessionImpl sessionImpl, String sql, ExecuteContext executeContext) {
        this.sessionManager = sessionManager;
        this.session = sessionImpl;
        this.sql = sql;
        this.executeContext = executeContext;
        this.applicationId = executeContext.getApplicationId();
    }

    protected void checkClosed() throws SQLException {
        if (closed) {
            throw new SQLException("No operations allowed execute_plan_optimizer statement closed.");
        }
    }

    // jdbc规范: 返回true表示executeQuery，false表示executeUpdate

    public boolean execute(String sql) throws SQLException {
        return executeInternal(sql, -1);
    }

    public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
        return executeInternal(sql, autoGeneratedKeys);
    }

    // jdbc规范: 返回true表示executeQuery，false表示executeUpdate
    protected boolean executeInternal(String sql, int autoGeneratedKeys) throws SQLException {
        OptimizerContext.setOptimizerContext(this.sessionManager.getConfigHolder().getOptimizerContext());
        boolean write = session.isWrite(sql);
        if (write) {
            if (autoGeneratedKeys != -1) {
                this.updateCount = executeUpdate(sql, autoGeneratedKeys);
            } else {
                this.updateCount = executeUpdate(sql);
            }
            return false;
        } else {
            executeQuery(sql);
            return true;
        }
    }

    /*
     * ========================================================================
     * executeQuery 查询逻辑 这里按照mysql connection逻辑，调用connection的executeSQL方法返回resultset
     * Session 的 execSQL方法
     * ======================================================================
     */

    public ResultCursor executeQuery(String sql) throws SQLException {
        checkClosed();
        ensureResultSetIsEmpty();
        currentResultSet = this.session.executeSQL(this.executeContext, sql, null, this, extraCmd);
        this.moreResults = false;
        return currentResultSet;
    }

    public int executeUpdate(String sql) throws SQLException {
        return executeUpdateInternal(sql, -1);
    }

    public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
        return executeUpdateInternal(sql, autoGeneratedKeys);
    }

    protected int executeUpdateInternal(String sql, int autoGeneratedKeys) throws SQLException {
        checkClosed();
        ensureResultSetIsEmpty();
        executeContext.setAutoGeneratedKeys(autoGeneratedKeys);

        currentResultSet = this.session.executeSQL(this.executeContext, sql, null, this, extraCmd);
        this.moreResults = false;
        return 0;// ((RowDataSet) resultSetWrapper).getAffectRows();
    }

    public void addBatch(String sql) throws SQLException {
        throw new UnsupportedOperationException("addBatch(String chars) is not supported, please use addBatch() in PreparedParseAndExecute");
    }

    public int[] executeBatch() throws SQLException {
        throw new UnsupportedOperationException("executeBatch");
    }

    public void clearBatch() throws SQLException {
        checkClosed();
        throw new UnsupportedOperationException("clearBatch() is not supported, please use clearBatch() in PreparedParseAndExecute");
    }

    public boolean isClosed() throws SQLException {
        return this.closed;
    }

    public void close() throws SQLException {
        close(true);
    }

    void close(boolean removeThis) throws SQLException {
        if (closed) {
            return;
        }
        try {
            if (currentResultSet != null) {
                currentResultSet.close(null);
            }

            if (removeThis) {
                session.removeStatement(this);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            currentResultSet = null;
        }
        closed = true;
    }

    /**
     * 如果新建了查询，那么上一次查询的结果集应该被显示的关闭掉。这才是符合jdbc规范的
     *
     * @throws SQLException
     */
    protected void ensureResultSetIsEmpty() throws SQLException {
        if (currentResultSet != null) {
            // log.debug("result setLimitValue is not null,close currentRowData result setLimitValue");
            try {
                currentResultSet.close(null);
            } catch (RuntimeException e) {
                log.error("exception on close last result setLimitValue . can do nothing..", e);
            } finally {
                // 最终要显示的关闭它
                currentResultSet = null;
            }
        }

        if (generatedKeysResults != null) {
            try {
                generatedKeysResults.close();
            } catch (SQLException e) {
                log.error("exception on close last result setLimitValue . can do nothing..", e);
            } finally {
                // 最终要显示的关闭它
                generatedKeysResults = null;
            }
        }
    }

    public int getMaxFieldSize() throws SQLException {
        return this.maxFieldSize;
    }

    public void setMaxFieldSize(int max) throws SQLException {
        this.maxFieldSize = max;

    }

    public int getMaxRows() throws SQLException {
        return this.maxRows;
    }

    public void setMaxRows(int max) throws SQLException {
        this.maxRows = max;
    }

    public ResultCursor getResultSet() throws SQLException {
        return currentResultSet;
    }

    public int getUpdateCount() throws SQLException {
        return updateCount;
    }

    public SessionImpl getSession() throws SQLException {
        return this.session;
    }

    public int getQueryTimeout() throws SQLException {
        return this.queryTimeOut;
    }

    public void setQueryTimeout(int seconds) throws SQLException {
        this.queryTimeOut = seconds;

    }

    public int getFetchSize() throws SQLException {
        return (int) GeneralUtil.getExtraCmdLong(extraCmd, ConnectionProperties.FETCH_SIZE, 1000);
    }

    public void setFetchSize(int rows) throws SQLException {
        extraCmd.put(ConnectionProperties.FETCH_SIZE, rows);
    }

    public boolean getMoreResults() throws SQLException {
        return moreResults;
    }

    public boolean getMoreResults(int current) throws SQLException {
        return moreResults;
    }

    public RowDataSet getGeneratedKeys() throws SQLException {
        if (this.executeContext == null || this.executeContext.getAutoGeneratedKeys() != java.sql.Statement.RETURN_GENERATED_KEYS) {
            throw new SQLException("Generated slotKeys not requested. You need to specify Execute.RETURN_GENERATED_KEYS to Execute.executeUpdate() or Session.prepareDataNodePreparedStatement().");
        }

        RowDataListResultCursor rowDataListResultCursor = new RowDataListResultCursor("GENERATED_KEYS", executeContext);
        rowDataListResultCursor.addColumn("GENERATED_KEY", Type.LongType);
        rowDataListResultCursor.initMeta();

        List<Long> generatedKeys = this.session.getGeneratedKeys();
        if (generatedKeys == null || generatedKeys.isEmpty()) {
            long last = this.session.getLastInsertId();
            Parameters parameters = this.executeContext.getParameters();
            if (parameters.isBatch()) {
                int size = this.executeContext.getParameters().getBatchSize();
                for (int i = 1; i <= size; i++) {
                    rowDataListResultCursor.addRowData(new Object[]{last - size + i});
                }
            } else {
                rowDataListResultCursor.addRowData(new Object[]{last});
            }
        } else {
            // 如果是直连下推模式，直接获取mysql返回的keys进行返回
            for (Long id : generatedKeys) {
                rowDataListResultCursor.addRowData(new Object[]{id});
            }
        }

        generatedKeysResults = new RowDataSet(new ResultCursor(rowDataListResultCursor, this.executeContext));
        return generatedKeysResults;
    }

    public void cancel() throws SQLException {
        this.session.cancelQuery();
    }

    public String getApplicationId() {
        return this.applicationId;
    }
}
